package com.ruoyi.system.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.utils.HttpUtils;
import com.ruoyi.system.domain.JWMajor;
import com.ruoyi.system.domain.JWMajorDTO;
import com.ruoyi.system.mapper.JWMajorMapper;
import com.ruoyi.system.service.JWMajorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;

import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import lombok.Data;

// 异常数据记录实体（内部类）
@Data
class MajorDataConvertError {
    private String wybs;       // 唯一标识
    private String fieldName;  // 异常字段名
    private String fieldValue; // 异常字段值
    private String errorReason;// 异常原因
}

/**
 * 专业建设数据同步业务实现（全量导入版，适配最新表结构）
 */
@Service
public class JWMajorServiceImpl implements JWMajorService {
    private static final Logger log = LoggerFactory.getLogger(JWMajorServiceImpl.class);

    // API配置
    private static final String TOKEN_URL = "https://data.neau.edu.cn/open_api/authentication/get_access_token";
    private static final String MAJOR_CONSTRUCTION_URL = "https://data.neau.edu.cn/open_api/customization/view_hotel_foxtrot/full";
    private static final String KEY = "20250714773319445588170900848643464";
    private static final String SECRET = "8ad2a72d4de5c95786203c0552563601842014be";

    @Autowired
    private JWMajorMapper majorMapper;

    // Token缓存与过期时间
    private String token;
    private LocalDateTime tokenExpireTime;

    /**
     * 通用分页获取接口数据
     */
    private <T> List<T> getAllApiData(String token, String url, Class<T> clazz) throws Exception {
        List<T> allData = new ArrayList<>();
        int page = 1;
        while (true) {
            String requestUrl = url + "?access_token=" + token + "&page=" + page;
            String resp = HttpUtils.sendGet(requestUrl);
            log.debug("第 {} 页数据接口响应：{}", page, resp);

            JSONObject json = JSONObject.parseObject(resp);
            if (!"10000".equals(json.getString("code"))) {
                throw new RuntimeException("第" + page + "页数据获取失败：" + json.getString("msg"));
            }

            JSONObject result = json.getJSONObject("result");
            JSONArray dataArr = result.getJSONArray("data");
            if (dataArr == null || dataArr.size() == 0) {
                break; // 无数据时终止分页
            }

            List<T> pageData = dataArr.toJavaList(clazz);
            allData.addAll(pageData);
            log.debug("第 {} 页数据解析完成，新增 {} 条", page, pageData.size());

            // 检查是否还有下一页
            int totalPages = result.getIntValue("max_page");
            if (page >= totalPages) {
                break;
            }
            page++;
        }
        return allData;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncAll() throws Exception {
        List<MajorDataConvertError> convertErrors = new ArrayList<>();

        try {
            log.info("===== 开始执行专业建设数据同步（全量导入模式） =====");

            // 1. 获取access_token
            String token = getAccessTokenWithRetry(3);
            log.info("成功获取token（脱敏显示）: {}", maskToken(token));

            // 2. 获取所有专业建设数据
            List<JWMajorDTO> majorList = getAllApiData(token, MAJOR_CONSTRUCTION_URL, JWMajorDTO.class);
            log.info("数据获取完成：专业建设信息 {} 条（全部导入，不做过滤）", majorList.size());

            // 3. 转换数据（全字段映射）
            List<JWMajor> allMajors = convertData(majorList, convertErrors);
            log.info("数据转换完成: 总导入数据 {} 条，转换异常记录 {} 条（仅记录）", allMajors.size(), convertErrors.size());

            // 4. 处理异常数据（仅记录，不影响导入）
            handleConvertErrors(convertErrors);

            // 5. 清空旧数据并插入新数据
            majorMapper.deleteAll();
            majorMapper.resetAutoIncrement();
            log.info("已清空历史数据");

            if (!allMajors.isEmpty()) {
                int batchSize = 100;
                batchInsertMajors(allMajors, batchSize);
                log.info("全量数据插入完成: 累计插入 {} 条", allMajors.size());
            } else {
                log.warn("无数据可插入（接口返回空数据）");
            }

            log.info("===== 专业建设数据同步（全量导入）成功完成 =====");

        } catch (Exception e) {
            log.error("===== 专业建设数据同步失败 =====", e);
            throw e;
        }
    }

    /**
     * 获取access_token（带重试机制）
     */
    private String getAccessTokenWithRetry(int maxRetry) throws Exception {
        int retryCount = 0;
        while (retryCount < maxRetry) {
            try {
                return getAccessToken();
            } catch (Exception e) {
                retryCount++;
                if (retryCount >= maxRetry) {
                    log.error("获取access_token重试 {} 次后失败", maxRetry, e);
                    throw new Exception("获取接口授权失败，已达最大重试次数", e);
                }
                log.warn("获取access_token第 {} 次失败，1秒后重试", retryCount, e);
                TimeUnit.SECONDS.sleep(1);
            }
        }
        throw new Exception("未获取到access_token");
    }

    /**
     * 原始获取access_token方法
     */
    private String getAccessToken() throws Exception {
        // 缓存未过期则直接返回
        if (token != null && LocalDateTime.now().isBefore(tokenExpireTime)) {
            return token;
        }

        // 重新获取token
        String resp = HttpUtils.sendGet(TOKEN_URL + "?key=" + KEY + "&secret=" + SECRET);
        log.debug("token接口响应原始内容：{}", resp);

        JSONObject json = JSONObject.parseObject(resp);
        if (!"10000".equals(json.getString("code"))) {
            throw new RuntimeException("获取token失败：" + json.getString("msg"));
        }

        JSONObject result = json.getJSONObject("result");
        this.token = result.getString("access_token");
        this.tokenExpireTime = LocalDateTime.now().plusHours(1); // 假设有效期1小时
        return this.token;
    }

    /**
     * 转换API数据为实体（全字段映射，适配最新表结构）
     */
    private List<JWMajor> convertData(
            List<JWMajorDTO> apiList,
            List<MajorDataConvertError> errors) {

        List<JWMajor> majors = new ArrayList<>();
        DateTimeFormatter timestampFmt = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

        for (JWMajorDTO api : apiList) {
            // 唯一标识处理（为空也保留数据）
            String wybs = api.getWYBS() != null ? api.getWYBS().trim() : null;
            if (wybs == null || wybs.isEmpty()) {
                errors.add(createError(null, "WYBS", null, "唯一标识为空"));
            }

            JWMajor major = new JWMajor();

            // 1. 基础字段映射（确保所有字段完整赋值）
            major.setMcMajorCode(api.getZYBM() != null ? api.getZYBM().trim() : null);
            major.setMcMajorName(api.getZYMC() != null ? api.getZYMC().trim() : null);
            major.setMcMajorEnglishName(api.getZYYWMC() != null ? api.getZYYWMC().trim() : null); // 专业英文名称
            major.setMcDeptCode(api.getYXBM() != null ? api.getYXBM().trim() : null); // 院系编码
            major.setMcDegreeType(api.getXKMLM() != null ? api.getXKMLM().trim() : null); // 学科门类码
            major.setMcUniqueId(wybs); // 唯一标识
            major.setMcInternalName(api.getXNZYMC() != null ? api.getXNZYMC().trim() : null); // 校内专业名称
            major.setMcYearOfEstablishment(api.getZYSZNF() != null ? api.getZYSZNF().trim() : null); // 设置年份（字符串类型）
            major.setMcStudyLength(api.getXZ() != null ? api.getXZ().trim() : null); // 学制
            major.setMcMaxStudyYears(api.getYXXYNX() != null ? api.getYXXYNX().trim() : null); // 允许修业年限
            major.setMcRecruitStatus(api.getZSZT() != null ? api.getZSZT().trim() : null); // 招生状态
            major.setMcMajorLevel(api.getZYJB() != null ? api.getZYJB().trim() : null); // 专业级别
            major.setMcInternalCode(api.getXNZYBM() != null ? api.getXNZYBM().trim() : null); // 校内专业编码（非空）
            major.setTimePin(api.getTSTAMP() != null ? api.getTSTAMP().trim() : null); // 时间戳（映射到time_pin字段）
            major.setMcRemarks(api.getBZ() != null ? api.getBZ().trim() : null); // 备注

            // 2. 项目相关字段（API未提供时保留空值）
            major.setMcEnrollmentCategory(null); // 招生所属大类（API无对应字段）
            major.setMcProjectName1(null); // 专业建设项目名称1
            major.setMcProjectTime1(null); // 项目1时间（datetime类型，API无对应格式数据）
            major.setMcProjectLevel1(null); // 项目1等级
            major.setMcAwardingDepartment1(null); // 项目1授予部门
            major.setMcProjectName2(null); // 专业建设项目名称2
            major.setMcProjectTime2(null); // 项目2时间
            major.setMcProjectLevel2(null); // 项目2等级
            major.setMcAwardingDepartment2(null); // 项目2授予部门

            // 3. 系统默认字段
            major.setAuditStatus("0"); // 默认未审核
            major.setUpdateTime(LocalDateTime.now()); // 更新时间（自动生成）
            major.setUserId(null); // 用户ID（未提供）
            major.setDeptId(null); // 部门ID（未提供）
            major.setRejectionReason(null); // 未通过原因（未提供）
            major.setWdUplocal(null); // 佐证材料（未提供）

            // 4. 时间戳格式校验（仅记录异常，不影响存储）
            if (api.getTSTAMP() != null && !api.getTSTAMP().trim().isEmpty()) {
                try {
                    // 仅校验格式，不转换（因time_pin为字符串类型）
                    LocalDateTime.parse(api.getTSTAMP().trim(), timestampFmt);
                } catch (DateTimeParseException e) {
                    errors.add(createError(wybs, "TSTAMP", api.getTSTAMP(), "时间戳格式不符合yyyyMMddHHmmss（仍存储原始值）"));
                }
            }

            // 5. 强制添加所有数据（无过滤）
            majors.add(major);
        }

        return majors;
    }

    /**
     * 分批次插入数据
     */
    private void batchInsertMajors(List<JWMajor> majors, int batchSize) {
        for (int i = 0; i < majors.size(); i += batchSize) {
            int end = Math.min(i + batchSize, majors.size());
            List<JWMajor> batch = majors.subList(i, end);
            majorMapper.batchInsert(batch);
            log.debug("已插入第 {} - {} 条数据", i + 1, end);
        }
    }

    /**
     * 处理转换异常数据（仅记录，不影响导入）
     */
    private void handleConvertErrors(List<MajorDataConvertError> errors) {
        if (errors.isEmpty()) {
            return;
        }

        // 1. 打印异常汇总日志
        log.warn("===== 数据转换异常汇总（共 {} 条，不影响导入） =====", errors.size());
        errors.forEach(error -> log.warn("唯一标识: {} | 异常字段: {} | 异常值: {} | 原因: {}",
                error.getWybs(),
                error.getFieldName(),
                truncateValue(error.getFieldValue(), 50),
                error.getErrorReason()));

        // 2. 保存异常数据到本地文件
        try {
            saveErrorsToFile(errors);
            log.info("异常数据已保存到本地日志文件");
        } catch (IOException e) {
            log.error("保存异常数据文件失败", e);
        }
    }

    /**
     * 保存异常数据到本地文件
     */
    private void saveErrorsToFile(List<MajorDataConvertError> errors) throws IOException {
        String fileName = "major_sync_errors_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".log";
        try (FileWriter writer = new FileWriter(fileName, true)) {
            for (MajorDataConvertError error : errors) {
                String logLine = String.format(
                        "[%s] 唯一标识: %s, 字段: %s, 异常值: %s, 原因: %s%n",
                        LocalDateTime.now(),
                        error.getWybs(),
                        error.getFieldName(),
                        error.getFieldValue(),
                        error.getErrorReason());
                writer.write(logLine);
            }
        }
    }

    /**
     * 创建异常记录
     */
    private MajorDataConvertError createError(String wybs, String fieldName, String fieldValue, String errorReason) {
        MajorDataConvertError error = new MajorDataConvertError();
        error.setWybs(wybs);
        error.setFieldName(fieldName);
        error.setFieldValue(fieldValue);
        error.setErrorReason(errorReason);
        return error;
    }

    /**
     * 脱敏token
     */
    private String maskToken(String token) {
        if (token == null || token.length() <= 8) {
            return token;
        }
        return token.substring(0, 4) + "***" + token.substring(token.length() - 4);
    }

    /**
     * 截断超长字符串
     */
    private String truncateValue(String value, int maxLength) {
        if (value == null || value.length() <= maxLength) {
            return value;
        }
        return value.substring(0, maxLength) + "...[省略" + (value.length() - maxLength) + "字符]";
    }
}